import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import dominion.Dominion;
import dominion.Player;
import dominion.player.NullPlayer;
import dominion.player.OneSmithyPlayer;
import dominion.player.WorkshopGardenPlayer;

public class Main {

	static final Logger LOG = LoggerFactory.getLogger(Main.class);
	static final Logger STAT = LoggerFactory.getLogger("stat");
	static final int TIMES = 100;

	static class Statistics {
		int littleDiff;
		BigDecimal moat = BigDecimal.ZERO;
	}

	public static void main(String[] args) {
		Map<String, BigDecimal> winners = new HashMap<String, BigDecimal>();
		Map<Integer, Integer> results = new HashMap<Integer, Integer>();
		BigDecimal result = new BigDecimal(0);
		Statistics stat = new Statistics();
		for (int i = 0; i < TIMES; i++) {
			int turn = simurate(winners, stat);
			result = result.add(BigDecimal.valueOf(turn));
			Integer count = results.get(turn);
			if (count == null) {
				count = 1;
			} else {
				count += 1;
			}
			results.put(turn, count);
		}
		BigDecimal avg = result.divide(BigDecimal.valueOf(TIMES));
		STAT.info("Avg. {}", avg.toPlainString());

		List<Integer> turns = new ArrayList<Integer>(results.keySet());
		Collections.sort(turns);
		for (Integer i : turns) {
			STAT.info("{} {}", i, results.get(i));
		}

		if (0 < stat.littleDiff) {
			STAT.info("secondly player buys one more Duchy will win {} times",
					stat.littleDiff);
		}
		if (BigDecimal.ZERO.equals(stat.moat) == false) {
			STAT.info("Moat protected Avg. {}", stat.moat.divide(BigDecimal
					.valueOf(TIMES)));
		}

		List<String> names = new ArrayList<String>(winners.keySet());
		Collections.sort(names);
		for (String s : names) {
			BigDecimal b = winners.get(s);
			if (b == null) {
				b = BigDecimal.ZERO;
			}
			BigDecimal a = b.divide(BigDecimal.valueOf(TIMES));
			STAT.info("{} Wins {} Times / Avg. {}", new Object[] { s,
					b.toPlainString(), a.toPlainString() });
		}
	}

	static int simurate(Map<String, BigDecimal> winners, Statistics stat) {
		int result = 0;
		Dominion dominion = new Dominion();
		dominion.add(new WorkshopGardenPlayer(dominion));
		dominion.add(new OneSmithyPlayer(dominion));
		dominion.add(new WorkshopGardenPlayer(dominion));
		dominion.add(new NullPlayer(dominion));
		// dominion.add(new NullPlayer(dominion));
		dominion.initialize();
		for (result = 0; dominion.isFinished() == false; result++) {
			dominion.round();
		}
		for (Player p : dominion.players()) {
			LOG.info("last deck {}:{}VP:{}", new Object[] { p.name(),
					p.victory(), p });
		}
		List<Player> rank = dominion.vpRank();
		if (1 < rank.size()) {
			Player first = rank.get(0);
			Player second = rank.get(1);
			int diff = first.victory() - second.victory();
			if (diff < 1) {
				if (first.turnCount() == second.turnCount()) {
					addWinner(winners, first.name());
					addWinner(winners, second.name());
				} else if (first.turnCount() < second.turnCount()) {
					addWinner(winners, first.name());
				} else {
					addWinner(winners, second.name());
				}
				if (2 < rank.size()) {
					LOG.info("{} and {} is same VP", first.name(), second
							.name());
					second = rank.get(2);
					diff = first.victory() - second.victory();
				}
			} else {
				addWinner(winners, first.name());
			}
			LOG.info("winner is {} secondly {} diff {}", new Object[] {
					first.name(), second.name(), diff });
			if (diff < 3) {
				stat.littleDiff++;
				LOG
						.info("if {} buy one more Duchy will be win.", second
								.name());
			}

			for (Player p : dominion.players()) {
				if (0 < p.protectedCount()) {
					BigDecimal avg = BigDecimal.valueOf(p.protectedCount())
							.divide(BigDecimal.valueOf(p.attcked()),
									RoundingMode.HALF_UP);
					LOG.info("{} protected by Moat Avg. {}", p.name(), avg
							.toPlainString());
					stat.moat = stat.moat.add(avg);
				}
			}
		}
		LOG.info("#####################################"
				+ "######  Game is end  ###############"
				+ "####################################");
		return result;
	}

	private static void addWinner(Map<String, BigDecimal> winners, String winner) {
		BigDecimal b = winners.get(winner);
		if (b == null) {
			b = BigDecimal.ONE;
		} else {
			b = b.add(BigDecimal.ONE);
		}
		winners.put(winner, b);
	}
}
